import { Component, OnDestroy, OnInit, EventEmitter, ViewChild, Input, SimpleChanges, OnChanges } from '@angular/core';
import { MatDialog } from '@angular/material';
import { MatPaginator, MatSort } from '@angular/material';
import { merge, of as observableOf } from 'rxjs';
import { Observable } from 'rxjs/Observable';
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { RestService } from '../../services/rest.service';
import { PersonDialogComponent } from '../person-dialog/person-dialog.component'
import { PwizardComponent } from '../pwizard/pwizard.component'
import { BindDialogComponent } from '../bind-dialog/bind-dialog.component'
import { BindBandDialogComponent } from '../bindband-dialog/bindband-dialog.component'
import { UnbindDialogComponent } from '../unbind-dialog/unbind-dialog.component'
import { UnbindBandDialogComponent } from '../unbindband-dialog/unbindband-dialog.component'
import { PtypeDialogComponent } from '../ptype-dialog/ptype-dialog.component'
import { PbasicDialogComponent } from '../pbasic-dialog/pbasic-dialog.component'
import { PcontactDialogComponent } from '../pcontact-dialog/pcontact-dialog.component'
import { PhealthDialogComponent } from '../phealth-dialog/phealth-dialog.component'
import { PhospitalDialogComponent } from '../phospital-dialog/phospital-dialog.component'
import { CheckoutDialogComponent } from '../checkout-dialog/checkout-dialog.component'
import { PersonDetailsComponent } from '../../mcenter/person-details/person-details.component';
import { TranslateService } from '@ngx-translate/core';
import { DialogTypes, ActionTypes } from '../../models/wdl-types';

import * as _ from 'lodash';
import { AlarmDialogComponent } from 'app/mcenter/alarm-dialog/alarm-dialog.component';

@Component({
  selector: 'app-personin',
  templateUrl: './personin.component.html',
  styleUrls: ['./personin.component.css'],
})
export class PersoninComponent implements OnInit, OnDestroy, OnChanges {
  @Input() selectionChange: any;

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  displayedColumns: string[] = ['number', 'name', 'sex', 'rid', 'building', 'level', 'room', 'bed', 'nurse', 'device1','device2', 'actions'];

  dataSource = [];
  searchText = '';
  resultsLength = 0;
  pageSize = 25;
  search: EventEmitter<any> = new EventEmitter();

  url = 'data/person';
  isLoadingResults = true;
  institution: any;
  user: any;

  constructor(public dialog: MatDialog,
              private translate: TranslateService,
              private rest: RestService) {
    this.institution = this.rest.getInstitution();
    this.user = this.rest.getUser();
  }

  isUserQualified() {
    return this.user.role === 'header';
  }

  informPersonChange(personId: string,devId:string, type: string) {
    // type could be checkin, checkout, delete, updateDevice, updateThreshold, updateBasic, updateContact, updateHospital
    this.rest.post('data/configData', {
      personId: personId,
      devId:devId,
      type: type
    }).subscribe(() => {
    });
  }

  bind(person) {
    this.dialog.open(BindDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '450px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data) || _.isEmpty(data)) {
        return;
      }

      const observableBatch = [];

      if (data.device1Id) {
        // now device1 is bound, check if original one should be unbinded
        if (person.device1Id) {
          // unbind it firstly
          observableBatch.push(this.rest.patch('data/device/' + person.device1, {personId: 0, personName: ''}));
        }

        observableBatch.push(this.rest.patch('data/device/' + data.device1, {personId: person.id, personName: person.personName}));
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, data));

      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Bind ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, data.device1Id,'bind');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('bindPersonError', message);
        this.refreshSearch();
      });
    });
  }
  bindband(person) {
    this.dialog.open(BindBandDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '450px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data) || _.isEmpty(data)) {
        return;
      }

      const observableBatch = [];

      if (data.device2Id) {
        // now device1 is bound, check if original one should be unbinded
        if (person.device2Id) {
          // unbind it firstly
          observableBatch.push(this.rest.patch('data/device/' + person.device2, {personId: 0, personName: ''}));
        }

        observableBatch.push(this.rest.patch('data/device/' + data.device2, {personId: person.id, personName: person.personName}));
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, data));

      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Bind ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, data.device2Id,'bind');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('bindPersonError', message);
        this.refreshSearch();
      });
    });
  }
  unbind(person) {
    this.dialog.open(UnbindDialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '450px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data) || _.isEmpty(data)) {
        return;
      }

      const observableBatch = [];

      const patchData = {};
      if (data.device1) {
        observableBatch.push(this.rest.patch('data/device/' + data.device1, {personId: 0, personName: ''}));
        patchData['device1'] = 0;
        patchData['device1Id'] = '';
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, patchData));
      console.log(data);
      console.log(person);
      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Unbind ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, person.device1Id,'unbind');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('unbindPersonError', message);
        this.refreshSearch();
      });
    });
  }
  unbindband(person) {
    this.dialog.open(UnbindBandDialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '450px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data) || _.isEmpty(data)) {
        return;
      }

      const observableBatch = [];

      const patchData = {};
      if (data.device2) {
        observableBatch.push(this.rest.patch('data/device/' + data.device2, {personId: 0, personName: ''}));
        patchData['device2'] = 0;
        patchData['device2Id'] = '';
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, patchData));

      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Unbind ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, person.device2Id,'unbind');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('unbindPersonError', message);
        this.refreshSearch();
      });
    });
  }
  remove(person) {
    const title = this.translate.get('deleteConfirmation')['value'];
    const message = this.translate.get('deletePersonMessage', {personName: person.personName})['value'];

    this.rest.openGenDialog(DialogTypes.CONFIRMATION, title, message, () => {
      this.rest.delete(this.url + '/' + person.id).subscribe(result => {
        // unbind everything
        const observableBatch = [];

        if (person['bedId'] > 0) {
          observableBatch.push(this.rest.patch('data/bed/' + person['bedId'], {personId: 0, personName: ''}));
        }

        if (person['device1Id']) {
          observableBatch.push(this.rest.patch('data/device/' + person['device1'], {personId: 0, personName: ''}));
        }

        if (observableBatch.length < 1) {
          this.informPersonChange(person.id, person.device1Id,'delete');
          this.refreshSearch();
          return;
        }

        Observable.forkJoin(observableBatch).subscribe((resp) => {
          console.log('Delete person ' + person.personName + ' successfully!');
          this.informPersonChange(person.id, person.device1Id,'delete');
          this.refreshSearch();
        }, resp => {
          this.rest.showHttpError('deletePersonError', resp);
          this.refreshSearch();
        });
      }, resp => {
        this.rest.showHttpError(this.translate.get('error')['value'], resp);
        this.refreshSearch();
      });
    });
  }

  checkin() {
    this.dialog.open(PwizardComponent, {
      disableClose: true,
      autoFocus: false,
      width: '740px'
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      this.rest.post(this.url, data).subscribe((resp) => {
        const personId = resp['id'];
        const observableBatch = [];

        observableBatch.push(this.rest.link(this.url + '/' + personId + '/institution', 'data/institution/' + this.institution.id));

        if (data['bedId'] > 0) {
          observableBatch.push(this.rest.patch('data/bed/' + data['bedId'], {personId: personId, personName: data.personName}));
        }

        if (data['device1Id']) {
          observableBatch.push(this.rest.patch('data/device/' + data['device1'], {personId: personId, personName: data.personName}));
        }

        Observable.forkJoin(observableBatch).subscribe(() => {
          console.log('Add person ' + data.personName + ' successfully!');
          this.informPersonChange(personId, data['device1Id'],'checkin');
          this.refreshSearch();
        }, message => {
          this.rest.showHttpError('AddPersonError', message);
          this.refreshSearch();
        });
      }, message => {
        this.rest.showHttpError('addPersonError', message);
      });
    });
  }

  checkout(person) {
    this.dialog.open(CheckoutDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '600px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      const observableBatch = [];

      const patchData = {
        checkoutRecord: data.checkoutRecord,
        checkoutTime: new Date().getTime()
      };

      if (person.device1Id && !person.unbind1Time) {
        patchData['unbind1Time'] = new Date().getTime();
        observableBatch.push(this.rest.patch('data/device/' + person.device1, {personId: 0, personName: ''}));
      }

      if (person.bedId) {
        observableBatch.push(this.rest.patch('data/bed/' + person.bedId, {personId: 0, personName: ''}));
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, patchData));

      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Checkout ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, person.device1Id,'checkout');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('checkoutPersonError', message);
        this.refreshSearch();
      });
    });
  }

  view(person) {
    this.dialog.open(PersonDialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '840px',
      data: {
        mode: ActionTypes.READ,
        person: person
      }
    });
  }

  modify(person) {
    this.dialog.open(PtypeDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '400px',
      data: {
        type: 'in'
      }
    }).afterClosed().subscribe(data => {
      if (!data) {
        return;
      }

      switch (data) {
      case 'basicInfo':
        return this.modifyBasicInfo(person);
      case 'contactInfo':
        return this.modifyContactInfo(person);
      case 'healthSetting':
        return this.modifyHealthSetting(person);
      case 'inHospitalInfo':
        return this.modifyInHospitalInfo(person);
      }
    });
  }

  modifyBasicInfo(person) {
    this.dialog.open(PbasicDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '600px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      this.rest.patch(this.url + '/' + person.id, data).subscribe(resp => {
        console.log('update basic info for ' + person.personName + ' successfully!');
        if (person.personName !== data.personName) {
          // Name is updated, update alarm, bed, device
          this.rest.getWithParams('data/alarm/search/personId', {
            personId: person.id
          }).subscribe(alarmResp => {
            alarmResp['_embedded']['alarms'].forEach(alarm => {
              this.rest.patch('data/alarm/' + alarm.id, { personName: data.personName }).subscribe(() => { });
            });
          });

          this.rest.getWithParams('data/bed/search/personId', {
            personId: person.id
          }).subscribe(bedResp => {
            bedResp['_embedded']['beds'].forEach(bed => {
              this.rest.patch('data/bed/' + bed.id, { personName: data.personName }).subscribe(() => { });
            });
          });

          this.rest.getWithParams('data/device/search/personId', {
            personId: person.id
          }).subscribe(deviceResp => {
            deviceResp['_embedded']['devices'].forEach(device => {
              this.rest.patch('data/device/' + device.id, { personName: data.personName }).subscribe(() => { });
            });
          });
        }

        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('modifyPersonError', message);
        this.refreshSearch();
      });
    });
  }

  modifyContactInfo(person) {
    this.dialog.open(PcontactDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '600px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      this.rest.patch(this.url + '/' + person.id, data).subscribe(resp => {
        console.log('update contact info for ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, person.device1Id,'updateContact');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('modifyPersonError', message);
        this.refreshSearch();
      });
    });
  }

  modifyHealthSetting(person) {
    this.dialog.open(PhealthDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '600px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      this.rest.patch(this.url + '/' + person.id, data).subscribe(resp => {
        console.log('update basic info for ' + person.personName + ' successfully!');
        this.informPersonChange(person.id, person.device1Id,'updateThreshold');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('modifyPersonError', message);
        this.refreshSearch();
      });
    });
  }

  modifyInHospitalInfo(person) {
    this.dialog.open(PhospitalDialogComponent, {
      disableClose: true,
      autoFocus: false,
      width: '800px',
      data: {
        person: person
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      const personId = person.id;
      const observableBatch = [];

      if (data['bedId'] > 0 && data['bedId'] !== person.bedId) {
        if (person.bedId > 0) {
          observableBatch.push(this.rest.patch('data/bed/' + person.bedId, {personId: 0, personName: ''}));
        }
        observableBatch.push(this.rest.patch('data/bed/' + data['bedId'], {personId: personId, personName: person.personName}));
      }

      observableBatch.push(this.rest.patch(this.url + '/' + person.id, data));

      Observable.forkJoin(observableBatch).subscribe((resp) => {
        console.log('Modify person hospital ' + data.personName + ' successfully!');
        this.informPersonChange(person.id, person.device1Id,'updateHospital');
        this.refreshSearch();
      }, message => {
        this.rest.showHttpError('ModifyPersonError', message);
        this.refreshSearch();
      });
    });
  }

  viewHist(person) {
    this.dialog.open(PersonDetailsComponent, {
      disableClose: true,
      autoFocus: false,
      width: '1200px',
      data: {
        person: person
      }
    });
  }

  refreshSearch() {
    this.paginator.pageIndex = 0;
    this.search.emit(this.searchText);
  }

  ngOnInit() {
    this.initPersion();
  }

  initPersion() {
    this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);

    merge(this.sort.sortChange, this.paginator.page, this.search)
    .pipe(
      startWith({}),
      switchMap(() => {
        this.isLoadingResults = true;

        const params = {
          institutionId: this.institution.id,
          checkoutTime: 1,
          page: this.paginator.pageIndex ? this.paginator.pageIndex : 0,
          size: this.paginator.pageSize ? this.paginator.pageSize : this.pageSize
        };

        if (this.sort.active && this.sort.direction) {
          params['sort'] = this.sort.active + ',' + this.sort.direction
        }

        if (this.searchText) {
          params['name'] = this.searchText;
          return this.rest.getWithParams(this.url + '/search/namein', params);
        } else {
          return this.rest.getWithParams(this.url + '/search/allin', params);
        }
      }),
      map(data => {
        this.isLoadingResults = false;
        this.resultsLength = data['page']['totalElements'];
        const persons = data['_embedded']['persons'];
        let number = this.paginator.pageIndex * this.paginator.pageSize;

        persons.forEach(person => {
          person['number'] = ++number;

          if (person['bedString']) {
            const bedNames = person['bedString'].split('##');
            person['building'] = bedNames.length > 0 ? bedNames[0] : '';
            person['level'] = bedNames.length > 1 ? bedNames[1] : '';
            person['room'] = bedNames.length > 2 ? bedNames[2] : '';
            person['bed'] = bedNames.length > 3 ? bedNames[3] : '';
          } else {
            person['building'] = '';
            person['level'] = '';
            person['room'] = '';
            person['bed'] = '';
          }
        });

        return persons;
      }),
      catchError(() => {
        this.isLoadingResults = false;
        return observableOf([]);
      })
    ).subscribe(data => this.dataSource = data);
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes && changes.selectionChange && changes.selectionChange.currentValue === 0) {
      this.initPersion();
    }
  }

  ngOnDestroy() {
  }
}
